//    Copyright (C) Kherty.  All rights reserved.
#region

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.Globalization;
using OpenLS.Core;
using OpenLS.Spreadsheet.Expressions;
using OpenLS.Spreadsheet.Functions;

#endregion

namespace OpenLS.Spreadsheet.Parsing
{
    enum LocalizationMode
    {
        NonLocalized,
        Localized,
    }
    /// <summary>
    /// Summary description for ExpressionParser.
    /// </summary>
    internal class ExpressionParser : GenericParser
    {
        private readonly LocalizationMode _localized;
        private readonly Workbook _workbook;
        private readonly Worksheet _worksheet;
        //  internal List<RangeReference> _parsedRangeReferences = new List<RangeReference>();
        public readonly Cell Caller;
        private CellReferenceMode cellReferenceMode;

        public ExpressionParser(Worksheet worksheet, Cell caller,  LocalizationMode localized, CellReferenceMode cellReferenceMode)
            : this(worksheet.Workbook, localized, cellReferenceMode)
        {
            Contract.Requires(worksheet != null);
            _worksheet = worksheet;
            Caller = caller;
        }
        public ExpressionParser(Workbook workbook, LocalizationMode localized, CellReferenceMode cellReferenceMode)
        {
            _workbook = workbook;
            CurrentToken = new StartToken();
            _lexer = new Lexer();
            _localized = localized;
            this.cellReferenceMode = cellReferenceMode;
        }

        private SpreadsheetContext SpreadsheetContext
        {
            get
            {
                if (_workbook != null)
                    return _workbook.Document.Context;
                return Worksheet.Workbook.Document.Context;
            }
        }

        private TokenId formulaArgumentSeparator
        {
            get
            {
                if (_localized == LocalizationMode.Localized)
                    return FormulaHelper.FormulaArgumentSeparatorIdLocalized;
                return TokenId.Coma;
            }
        }

        public string Error { get; private set; }

        private Worksheet Worksheet
        {
            get { return _worksheet; }
        }

        private Expression concatenateExpression()
        {
            Expression r = additionExpression();
            while (Has(TokenId.Concatenate))
            {
                r = new ConcatenateExpression(r, additionExpression());
            }
            return r;
        }


        private Expression atomicExpression()
        {
            return rangeIntersectionExpression();
        }

        private Expression powerExpression()
        {
            return binaryExpression(unaryExpression, TokenId.Power, BinaryOperatorType.Power);
        }

        private Expression binaryExpression(Func<Expression> getter, TokenId id, BinaryOperatorType op)
        {
            Expression result = getter();
            while (Has((id)))
            {
                result = new BinaryExpression(result, op, getter());
            }
            return result;
        }

        private UnqualifiedExpressionBase cellExpression(RangeFactory factory)
        {
            UnqualifiedExpressionBase result = simpleExpression(factory);

            return result;
        }


        private Expression otherSheetRangeExpression()
        {
            Token oldCurrentToken = CurrentToken;
            if (Has(TokenId.LeftSquareBracket))
            {
                if (NextIs(TokenId.Numeric))
                {
                    var externalReferenceId = (int) Numeric().DoubleValue;
                    Eat(TokenId.RightSquareBracket);
                    string sheetName = null;
                    if (NextIs(TokenId.Identifier))
                        sheetName = Identifier().Value;
                    else if (Has(TokenId.Exclamation))
                    {
                        string name = Identifier().Value;
                        if (externalReferenceId == 0)
                        {
                            return new NameExpression(_workbook, name);
                        }
                        // We have a 
                        ExternalWorkbook book = _workbook.GetExternalLink(externalReferenceId).ExternalBook;
                        ///\\ var dn = Enumerable.First(book.ExternalDefinedNames, nn => nn.Name == name);
                        List<Expression> parms = null;
                        if (NextIs(TokenId.LeftParenthesis))
                            parms = parenthesizedExpressions();

                        return new ExternalNameExpression(_workbook, book, name, parms);
                    }
                    if (Has(TokenId.Exclamation))
                    {
                        ExternalLink link = _workbook.GetExternalLink(externalReferenceId);
                        RangeFactory factory = new SingleWorksheetRangeFactory(
                            link.ExternalBook.GetWorksheet(sheetName), _worksheet, this.cellReferenceMode);
                        return rangeExpression(factory).QualifiedExpression(factory);
                    }
                }
            }

            CurrentToken = oldCurrentToken;
            UnqualifiedExpressionBase result = rangeExpression(RangeFactory.Create(null, Worksheet,  cellReferenceMode));
            if (result is ColumnExpression && NextIs(TokenId.Identifier))
            {
                string s = Identifier().Value;
                if (!Has(TokenId.Exclamation))
                    throw new ParsingException(null, "needs !");
                RangeFactory parentworksheet = new SingleWorksheetRangeFactory(_workbook.GetWorksheet(s), _worksheet, cellReferenceMode);
                return rangeExpression(parentworksheet).QualifiedExpression(parentworksheet);
            }
            if (NextIs(TokenId.Exclamation))
            {
                Has(TokenId.Exclamation);
                return rangeExpression(getRangeFarctory(result)).QualifiedExpression(getRangeFarctory(result));
            }
            RangeFactory factory2 = RangeFactory.Create(null, Worksheet, cellReferenceMode);
            if (result is UnqualifiedNameExpression)
            {
                var ne = (UnqualifiedNameExpression) result;
                if (Worksheet.Workbook.GetWorksheet(ne.Name) != null)
                {
                    var wss = new List<Worksheet> {Worksheet.Workbook.GetWorksheet(ne.Name)};
                    while (Has(TokenId.Colon))
                    {
                        ne = (UnqualifiedNameExpression)rangeExpression(RangeFactory.Create(null, Worksheet, cellReferenceMode));
                        wss.Add(Worksheet.Workbook.GetWorksheet(ne.Name));
                    }
                    factory2 = new MultiWorksheetRangeFactory(wss, _worksheet);
                    Expect(TokenId.Exclamation);
                    return rangeExpression(factory2).QualifiedExpression(factory2);
                }
            }
            return result.QualifiedExpression(factory2);
        }

        private RangeFactory getRangeFarctory(UnqualifiedExpressionBase expression)
        {
            Contract.Ensures(Contract.Result<RangeFactory>() != null);
            string value = null;
            if (expression is StringConstantExpression)
            {
                var sce = (StringConstantExpression) expression;
                value = ((sce.StringValue));
            }
            if (expression is NameExpression)
            {
                var sce = (NameExpression) expression;
                value = ((sce.Name));
            }
            if (expression is SheetNameExpression)
            {
                var sce = (SheetNameExpression) expression;
                value = ((sce.SheetName));
            }
            if (expression is UnqualifiedNameExpression)
            {
                var ne = (UnqualifiedNameExpression) expression;
                value = ne.Name;
            }
            if (value != null)
            {
                string[] values;
                values = expression is StringConstantExpression ? new[] {value} : value.Split(':');

                Contract.Assert(values.Length > 0);
                if (values.Length == 1)
                    return getSimpleRangeFactory(value);
                Debug.WriteLine(value + " ... " + expression);
                var worksheets =
                    new List<Worksheet>(Enumerable.Select(values, name => _workbook.GetWorksheet(name)));
                return new MultiWorksheetRangeFactory(worksheets, _worksheet);
            }

            if (expression is MultiNameExpression)
            {
                var mne = (MultiNameExpression) expression;
                var worksheets = new List<Worksheet>(Enumerable.Select(mne.Names, name => _workbook.GetWorksheet(name)));
                return new MultiWorksheetRangeFactory(worksheets, _worksheet);
            }
            if (expression is UnqualifiedRangeReferenceExpression)
            {
                var rangeReferenceExpression = (UnqualifiedRangeReferenceExpression) expression;
                return getSimpleRangeFactory(rangeReferenceExpression.OriginalText);
            }
            throw new NotSupportedException(expression.ToString());
        }

        private RangeFactory getSimpleRangeFactory(string value)
        {
            Contract.Requires(value != null);
            Contract.Ensures(Contract.Result<RangeFactory>() != null);
            if (value.StartsWith("["))
            {
                string s = value.Substring(1, value.Length - 1);
                string[] ss = s.Split(']');
                int sheetIndex = int.Parse(ss[0]);
                ExternalLink link = _workbook.GetExternalLink(sheetIndex);
                Worksheet result = link.ExternalBook.GetWorksheet(ss[1]);
                if (result == null)
                {
                    result = link.ExternalBook.GetWorksheet(ss[1]);
                    throw new InternalParsingException();
                }
                return new SingleWorksheetRangeFactory(result, _worksheet, cellReferenceMode);
            }
            else
            {
                Worksheet result = _workbook.GetWorksheet(value);
                if (result == null)
                {
                    result = _workbook.GetWorksheet(value);
                    throw new InternalParsingException();
                }
                return new SingleWorksheetRangeFactory(result, _worksheet, cellReferenceMode);
            }
        }

        private UnqualifiedExpressionBase rangeExpression(RangeFactory factory)

        {
            UnqualifiedExpressionBase result = multiCellExpression(factory);
            bool stop = false;
            while (result is UnqualifiedCellsAccessExpression && !stop)
            {
                TokenId tokenType = NextTokenType();
                switch (tokenType)
                {
                    case TokenId.Dollar:
                    case TokenId.Identifier:
                    case TokenId.Numeric:
                        {
                            Token position = CurrentToken;
                            UnqualifiedExpressionBase nextExpression = multiCellExpression(factory);
                            if (nextExpression is UnqualifiedCellsAccessExpression)
                            {
                                result =
                                    new UnqualifiedRangeIntersectionExpression(
                                        (UnqualifiedCellsAccessExpression) result,
                                        (UnqualifiedCellsAccessExpression) nextExpression);
                            }
                            else
                            {
                                stop = true;
                                SetCurrentToken(position);
                            }
                        }
                        break;
                    default:
                        stop = true;
                        break;
                }
            }
            return result;
        }


        private Expression compositeRangeExpression()
        {
            Expression result = otherSheetRangeExpression();
            while (Has(TokenId.Colon))
            {
                result = new BoundingRangeExpression(result,
                                                     otherSheetRangeExpression());
            }
            return result;
        }

        private Expression rangeIntersectionExpression()
        {
            Expression result = compositeRangeExpression();
            while (Has(TokenId.Space))
                result = new RangeIntersectionExpression(result, compositeRangeExpression());
            return result;
        }

        private UnqualifiedExpressionBase multiCellExpression(RangeFactory factory)
        {
            return cellExpression(factory);
        }

        private Expression divisionOrModulusExpression()
        {
            Expression r = powerExpression();
            while (NextIs(TokenId.Divide))
            {
                if (Has(TokenId.Divide))
                {
                    //r = newBinaryExpression(r,BinaryOperatorType.Divide, divisionOrModulusExpression ());
                    Expression ee = powerExpression();
                    if (ee is BinaryExpression && ((BinaryExpression) ee).Operator == BinaryOperatorType.Divide)
                    {
                        var ee2 = (BinaryExpression) ee;
                        r = new BinaryExpression(new BinaryExpression(r, BinaryOperatorType.Divide,
                                                                      ee2.Left), BinaryOperatorType.Divide, ee2.Right);
                    }
                    else
                    {
                        r = new BinaryExpression(r, BinaryOperatorType.Divide, ee);
                    }
                }
            }
            return r;
        }

        private Expression multiplicationExpression()
        {
            Expression r = divisionOrModulusExpression();
            while (Has(TokenId.Multiply))
            {
                r = new BinaryExpression(r, BinaryOperatorType.Multiply, divisionOrModulusExpression());
            }
            return r;
        }

        private Expression subtractionExpression()
        {
            Expression r = multiplicationExpression();
            while (Has(TokenId.Subtraction))
            {
                Expression ee = multiplicationExpression();
                {
                    if (Error != null)
                        return null;
                    r = new BinaryExpression(r, BinaryOperatorType.Subtract, ee);
                }
            }
            return r;
        }

        private Expression additionExpression()
        {
            Expression r = subtractionExpression();
            while (Has(TokenId.Addition))
            {
                Expression ee = additionExpression();
                {
                    if (Error != null)
                        return null;
                    r = new BinaryExpression(r, BinaryOperatorType.Add, ee);
                }
            }
            return r;
        }

        private Expression relationalExpression(Expression r)
        {
            bool b = false;
            switch (NextTokenType())
            {
                case TokenId.LessThan:
                case TokenId.GreaterThan:
                case TokenId.LessThanOrEqual:
                case TokenId.GreaterThanOrEqual:
                    b = true;
                    break;
            }
            while (b)
            {
                if (Has(TokenId.LessThan))
                    r = new BinaryExpression(r, BinaryOperatorType.LessThan, relationalExpression());
                else if (Has(TokenId.GreaterThan))
                    r = new BinaryExpression(r, BinaryOperatorType.GreaterThan, relationalExpression());
                else if (Has(TokenId.LessThanOrEqual))
                    r = new BinaryExpression(r, BinaryOperatorType.LessThanOrEqual, relationalExpression());
                else if (Has(TokenId.GreaterThanOrEqual))
                    r = new BinaryExpression(r, BinaryOperatorType.GreaterThanOrEqual, relationalExpression());
                else
                {
                    // Contract.Assert(false);
                }
                b = false;
                switch (NextTokenType())
                {
                    case TokenId.LessThan:
                    case TokenId.GreaterThan:
                    case TokenId.LessThanOrEqual:
                    case TokenId.GreaterThanOrEqual:
                        b = true;
                        break;
                }
            }
            return r;
        }

        private Expression relationalExpression()
        {
            return relationalExpression(concatenateExpression());
        }

        private Expression equalExpression()
        {
            Expression r = relationalExpression();
            while (NextIs(TokenId.Equality) || NextIs(TokenId.Inequality))
            {
                if (Has(TokenId.Equality))
                {
                    Expression right = equalExpression();
                    r = new BinaryExpression(r, BinaryOperatorType.Equality, right);
                }
                else if (Has(TokenId.Inequality))
                {
                    r = new BinaryExpression(r, BinaryOperatorType.Inequality, equalExpression());
                }
                // else
                    //Contract.Assert(false);
            }
            return r;
        }

        private Expression binaryAndExpression()
        {
            Expression r = equalExpression();
            return r;
        }

        private Expression binaryOrExpression()
        {
            Expression r = binaryAndExpression();
            return r;
        }

        private Expression andExpression()
        {
            Expression r = binaryOrExpression();
            while (Has(TokenId.And))
            {
                r = new BinaryExpression(r, BinaryOperatorType.And, andExpression());
            }
            return r;
        }

        private Expression xorExpression()
        {
            Expression r = andExpression();
            return r;
        }

        private Expression topExpression()
        {
            return orExpression();
        }

        private Expression orExpression()
        {
            Expression r = xorExpression();
            while (Has(TokenId.Or))
            {
                r = new BinaryExpression(r, BinaryOperatorType.Or, orExpression());
            }
            return r;
        }
        bool _hadEqual;

        internal Expression Expression(string text)
        {
            text = text.Replace("\b", "");
            if (text.StartsWith("'"))
                return new StringConstantExpression(text.Substring(1), true);
            _lexer.SetInput(text);
            if (text.Length == 0)
                return null;
            TokenId id = NextTokenType();
            if (id == TokenId.Equality)
                return assignedExpression();
            double d;
            if (DoubleUtil.TryParse(text, out d))
                return new DoubleConstantExpression(text, d);
            Fraction f;
            {
                if (text.Trim().IndexOf(' ') > 0 && Fraction.TryParse(text, out f))
                {
                    int i1 = text.IndexOf('/');
                    if (i1 >= 0)
                        i1 = text.IndexOf('/', i1 + 1);
                    else
                        i1 = 0;
                    if (f.Numerator > 0 && f.Numerator <= 12 && text.IndexOf('/') == 0 && i1 >= 0)
                    {
                        try
                        {
                            var dateTime = new DateTime(DateTime.Now.Year, f.Numerator, f.Denominator);
                            return new DateTimeConstantExpression(text, dateTime);
                        }
                        catch
                        {
                        }
                    }
                    return new FractionExpression(f);
                }
            }
            DateTime dt;
            if (DateTime.TryParse(text, out dt))
            {
                DateTime timeOnly;
                if (DateTime.TryParse(text, null, DateTimeStyles.NoCurrentDateDefault, out timeOnly))
                {
                    if (timeOnly.Year == 1 && timeOnly.Month == 1 && timeOnly.Day == 1)
                        return new TimeConstantExpression(text, timeOnly);
                }
                return new DateTimeConstantExpression(text, dt);
            }
            return new StringConstantExpression(text);
        }

        internal Expression Expression()
        {
            TokenId id = NextTokenType();
            switch (id)
            {
                case TokenId.Equality:
                    return assignedExpression();
                case TokenId.Identifier:
                    {
                        string s = (Identifier().Value);
                        CellAddress address;
                        if (CellAddress.TryParse(s, out address))
                        {
                            throw new NotImplementedException();
                            //   return new RangeReferenceExpression(Worksheet, address, address);//\\
                        }


                        return new StringConstantExpression(s);
                    }
                case TokenId.Numeric:
                    return Numeric();
                case TokenId.Subtraction:
                    Eat(TokenId.Subtraction);
                    Expression exp = topExpression();
                    if (exp is DoubleConstantExpression)
                    {
                        var d = (DoubleConstantExpression) exp;
                        return new DoubleConstantExpression("-" + d.OriginalString, -d.DoubleValue);
                    }
                    throw new NotImplementedException();
                default:
                    throw new NotImplementedException();
            }
        }

        private Expression assignedExpression()
        {
            Eat(TokenId.Equality);
            return orExpression();
        }

        private ErrorExpression getError(string identifier)
        {
            switch (identifier)
            {
                case "#VALUE":
                    Eat(TokenId.Exclamation);
                    return new ErrorExpression(Errors.ValueError);
                case "#REF":
                    Eat(TokenId.Exclamation);
                    return new ErrorExpression(Errors.RefError);
                case "#DIV":
                    if (Has(TokenId.Divide))
                        Expect(TokenId.Numeric);
                    Eat(TokenId.Exclamation);
                    return new ErrorExpression(Errors.DivError);
                case "#NA":
                    Has(TokenId.Exclamation); //\\remove?
                    return new ErrorExpression(Errors.NaError);
                case "#N":
                    if (Has(TokenId.Divide))
                        Eat(TokenId.Identifier);
                    Has(TokenId.Exclamation); //\\remove?
                    return new ErrorExpression(Errors.NaError);
                case "#NAME":
                case "#NAME?":
                    Token token = NextToken();
                    if (token != null && token.Type == TokenId.Identifier)
                    {
                        var id = (IdentifierBase) token;
                        if (id.Value == "?")
                            Eat(TokenId.Identifier);
                    }
                    return new ErrorExpression(Errors.NameError);
                case "#NUM":
                    Eat(TokenId.Exclamation);
                    return new ErrorExpression(Errors.NumError);
                case "#NULL":
                    Eat(TokenId.Exclamation);
                    return new ErrorExpression(Errors.NumError);
                    //\\todo: rest
            }
            return null;
        }

        private UnqualifiedExpressionBase simpleExpression(RangeFactory factory)
        {
            Token firstToken = CurrentToken;
            List<ReferenceToken> rangeTokens = _workbook.NamingConvention.ParseRangeReference(this);
            if (rangeTokens != null)
            {
                Token lastToken = CurrentToken;
                string s = _lexer.Substring(firstToken.Position + firstToken.Length,
                                            lastToken.Position + lastToken.Length - firstToken.Position -
                                            firstToken.Length);
                if (rangeTokens.Count == 1 && rangeTokens[0] is CellReferenceToken)
                {
                    if (NextIs(TokenId.LeftParenthesis))
                    {
                        Function f = SpreadsheetContext.GetFunction(s, _localized == LocalizationMode.Localized);
                        if (f != null)
                            return new FunctionInvokeExpression(f, ParenthesizedExpressionsAsArray(), SpreadsheetContext);
                        return new FunctionInvokeExpression(new UnknownFunction(s), ParenthesizedExpressionsAsArray(),
                                                            SpreadsheetContext);
                    }
                }
                return new UnqualifiedRangeReferenceExpression(rangeTokens, s);
            }

            TokenId te = NextTokenType();
            switch (te)
            {
                case TokenId.LeftParenthesis:
                    return expressionStartingWithParenthesis();
                case TokenId.LeftCurlyBracket:
                    return arrayExpression();
                case TokenId.Numeric:
                    CurrentToken = NextToken();
                    var numeric = (Numeric) CurrentToken;
                    double dbl = Convert.ToDouble(numeric.Value);

                    var doubleConstantExpression = new DoubleConstantExpression(numeric.OriginalString, dbl);
                    if (Has(TokenId.Numeric))
                    {
                        var numerator = (Numeric) CurrentToken;
                        Expect(TokenId.Divide);
                        Expect(TokenId.Numeric);
                        var denominator = (Numeric) CurrentToken;
                        var fraction = new Fraction(numeric.ValueAsInteger, numerator.ValueAsInteger,
                                                    denominator.ValueAsInteger);
                        return new FractionExpression(fraction);
                    }
                    if (Has(TokenId.Percent))
                    {
                        doubleConstantExpression = new DoubleConstantExpression(numeric.OriginalString + "%", dbl/100);
                    }
                    return doubleConstantExpression;
            }
            RangeFactory addressedWorksheet = RangeFactory.Create(factory, Worksheet, cellReferenceMode);
            const bool otherWorksheet = false;
            if (Has(TokenId.LeftSquareBracket))
            {
                if (NextIs(TokenId.Identifier))
                {
                    IdentifierBase id = Identifier();
                    Eat(TokenId.RightSquareBracket);
                    return new ColumnExpression(addressedWorksheet.Worksheet, id.Value);
                }
                if (NextIs(TokenId.Numeric) && addressedWorksheet != null)
                {
                    DoubleConstantExpression id = Numeric();
                    Eat(TokenId.RightSquareBracket);
                    return new ColumnExpression(addressedWorksheet.Worksheet, id.OriginalString);
                }
                int? externalReferenceId = (int) Numeric().DoubleValue;
                Eat(TokenId.RightSquareBracket);
                if (Has(TokenId.Exclamation))
                {
                    var wws = (Worksheet) _workbook.Sheets[(int) externalReferenceId];

                    string s = Identifier().Value;
                    return new NameExpression(wws, s);
                }
            }
            if (NextIs(TokenId.Identifier))
            {
                IdentifierBase identifier = Identifier();
                string s = identifier.Value;
                ErrorExpression errorExpression = getError(s);
                if (errorExpression != null)
                    return errorExpression;

                if (!otherWorksheet && NextIs(TokenId.LeftParenthesis)) //\\check
                {
                    Function f = SpreadsheetContext.GetFunction(s, _localized == LocalizationMode.Localized);
                    if (f != null)
                        return new FunctionInvokeExpression(f, ParenthesizedExpressionsAsArray(), SpreadsheetContext);
                    return new FunctionInvokeExpression(new UnknownFunction(s), ParenthesizedExpressionsAsArray(),
                                                        SpreadsheetContext);
                }
                {
                    switch (s.ToUpper())
                    {
                        case "TRUE":
                            return new BooleanConstantExpression(true);
                        case "FALSE":
                            return new BooleanConstantExpression(false);
                    }

                    if (Has(TokenId.LeftSquareBracket))
                    {
                        MultiRowOrColumnAccessor rowExpression = multiRowOrColumnExpression();
                        Expect(TokenId.Coma);
                        MultiRowOrColumnAccessor columnExpression = multiRowOrColumnExpression();
                        Expect(TokenId.RightSquareBracket);
                        return new TableAccessExpression(s, rowExpression, columnExpression);
                    }
                    if (addressedWorksheet == null)
                        return new StringConstantExpression(s);
                    if (s.Contains("%")) //\\
                        return new SheetNameExpression(s);
                    var nameExpression = new NameExpression(addressedWorksheet.Worksheet, s);
                    return nameExpression;
                }
            }
            switch (NextTokenType())
            {
                case TokenId.ExplicitCharacter:
                    CurrentToken = NextToken();
                    return new StringConstantExpression(((ExplicitCharacter) CurrentToken).Value.ToString());
                case TokenId.ExplicitString:
                    {
                        CurrentToken = NextToken();
                        var explicitString = (ExplicitString) CurrentToken;
                        var stringConstantExpression = new StringConstantExpression(explicitString.Value);
                        return stringConstantExpression;
                    }
                case TokenId.@true:
                    Expect(TokenId.@true);
                    return new BooleanConstantExpression(true);
                case TokenId.@false:
                    Expect(TokenId.@false);
                    return new BooleanConstantExpression(false);
            }
            if (CurrentToken is IdentifierBase)
                return new UnqualifiedNameExpression(((IdentifierBase) CurrentToken).Value);
            string text = "<empty>";
            Token t = NextToken();
            if (t is Exclamation)
            {
                var ws = (Worksheet) _workbook.Sheets[0];
                return new SheetNameExpression(ws.Name);
            }
            if (t != null)
                text = t.ToString();
            throw new UnexpectedTokenException(CurrentPragma, TokenId.Invalid, CurrentToken,
                                               new Exception(text + " is not handled"));
        }

        private Expression unaryExpression()
        {
            TokenId te = NextTokenType();
            switch (te)
            {
                case TokenId.Subtraction:
                    Eat(TokenId.Subtraction);
                    Expression iexp = unaryExpression();
                    if (iexp is DoubleConstantExpression)
                    {
                        var nn = (DoubleConstantExpression) iexp;
                        {
                            var pr = new DoubleConstantExpression("-" + nn.OriginalString, -nn.DoubleValue);
                            return pr;
                        }
                    }
                    return new PrefixUnaryExpression(UnaryOperatorType.Subtract, iexp);
                case TokenId.Addition:
                    Eat(TokenId.Addition);
                    iexp = unaryExpression();
                    if (iexp is DoubleConstantExpression)
                    {
                        var nn = (DoubleConstantExpression) iexp;
                        {
                            var pr = new DoubleConstantExpression("+" + nn.OriginalString, nn.DoubleValue);
                            return pr;
                        }
                    }
                    return new PrefixUnaryExpression(UnaryOperatorType.Add, iexp);
                default:
                    Expression result = atomicExpression();
                    if (Has(TokenId.Percent))
                        return new PercentExpression(result);
                    return result;
            }
        }


        private MultiRowOrColumnAccessor multiRowOrColumnExpression()
        {
            var accessors = new List<RowOrColumnAccessor> {simpleRowOrColumnExpression()};
            while (NextIs(TokenId.Colon) ||
                   (NextIs(TokenId.Identifier) && ((IdentifierBase) NextToken()).Value == ":"))
            {
                if (NextIs(TokenId.Colon))
                    Eat(TokenId.Colon);
                else
                    Eat(TokenId.Identifier);
                accessors.Add(simpleRowOrColumnExpression());
            }
            Debug.WriteLine(NextTokenType());
            Debug.WriteLine(NextIs(TokenId.Coma));
            Token nextToken = NextToken();
            if (nextToken is IdentifierBase)
                Debug.WriteLine(((IdentifierBase) nextToken).Value);
            Contract.Assert(NextIs(TokenId.RightSquareBracket) || NextIs(TokenId.Coma));
            return new MultiRowOrColumnAccessor(accessors);
        }

        private RowOrColumnAccessor simpleRowOrColumnExpression()
        {
            Expect(TokenId.LeftSquareBracket);
            int startPos = _lexer.Position;
            while (!NextIs(TokenId.RightSquareBracket))
            {
                SetCurrentToken(NextToken());
            }
            int endPos = _lexer.Position;
            Expect(TokenId.RightSquareBracket);
            return new RowOrColumnAccessor(_lexer.Substring(startPos, endPos - startPos - 1));
        }

        private Expression arrayExpression()
        {
            Eat(TokenId.LeftCurlyBracket);
            var result = new ArrayExpression();
            while (true)
            {
                if (Has(TokenId.RightCurlyBracket))
                {
                    return result;
                }
                result.Add(topExpression());
                if (Has(TokenId.SemiColon))
                {
                    result.AddRow();
                }
                else if (Has(formulaArgumentSeparator) )
                {
                    //\\
                }
            }
        }

        public int ReadInteger()
        {
            DoubleConstantExpression n = Numeric();
            return int.Parse(n.OriginalString);
        }

        public DoubleConstantExpression Numeric()
        {
            CurrentToken = NextToken();
            var n = (Numeric) CurrentToken;
            var result = new DoubleConstantExpression(n.OriginalString, Convert.ToDouble(n.Value));
            return result;
        }

        private Expression expressionStartingWithParenthesis()
        {
            Token t = CurrentToken;
            Expect(TokenId.LeftParenthesis);
            if (true)
            {
                CurrentToken = t;
                return parenthesizedExpression();
            }
        }

        private Expression parenthesizedExpression()
        {
            Expect(TokenId.LeftParenthesis);
            Expression result = topExpression();
            while (!NextIs(TokenId.RightParenthesis))
            {
                if (Has(TokenId.Coma))
                    result = new RangeListExpression(result, topExpression());
                else
                {
                    result = new RangeIntersectionExpression(result, topExpression());
                }
                //result = MultiRangeReferenceExpression.Combine(result, topExpression()); //\\sure? combineRangeReferenceExpressions(result, topExpression());
            }
            Expect(TokenId.RightParenthesis);
            return new ParenthesizedExpression(result);
        }


        private List<Expression> parenthesizedExpressions()
        {
            Expect(TokenId.LeftParenthesis);
            var r = new List<Expression>();
            while (!NextIs(TokenId.RightParenthesis))
            {
                if (Has(formulaArgumentSeparator))
                {
                    r.Add(new MissingExpression());
                    continue;
                }
                if (NextToken() == null)
                    break;
                r.Add(topExpression());
                if (!Has(formulaArgumentSeparator))
                    break;
            }
            if (CurrentToken != null && CurrentToken.Type == formulaArgumentSeparator)
                r.Add(new MissingExpression());
            Expect(TokenId.RightParenthesis);
            return r;
        }

        internal List<Expression> ParenthesizedExpressionsAsArray()
        {
            List<Expression> c = parenthesizedExpressions();
            return c;
        }
    }
}